package com.xxx.delayjob.util;

import org.joda.time.*;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * JodaTime日期时间工具类
 */
public class JodaUtil {
	
	/******************常用的日期和时间格式，缺少的可以自行添加*************************/
	public static final String DATETIME_PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss";
	public static final String DATE_PATTERN_DEFAULT = "yyyy-MM-dd";
	public static final String TIME_PATTERN_DEFAULT = "HH:mm:ss";
	public static final String DATE_PATTERN_YYYYMMDD = "yyyyMMdd";
	public static final String DATETIME_PATTERN_YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
	public static final String DATETIME_PATTERN_YYYYMMDDHHMMSSSSS = "yyyyMMddHHmmssSSS";
	
	// 私有构造方法，防止被实例化
	private JodaUtil() {
	}

	/****************** 创建日期时间对象 start ************************/
	/**
	 * 创建当前日期对象（不带参数）
	 * @return
	 */
	public static DateTime now() {
		return new DateTime();
	}
	
	/**
	 * 获得今天零时零分零秒
	 * @return
	 */
	public static DateTime get0OclockOfToday() {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		Date date = calendar.getTime();
		return new DateTime(date);
	}
	
	/**
	 * 创建指定的日期对象（带参数）
	 * @param year
	 * @param monthOfYear
	 * @param dayOfMonth
	 * @param hourOfDay
	 * @param minuteOfHour
	 * @param secondOfMinute
	 * @return
	 */
	public static DateTime getDateTime(int year,
					            	   int monthOfYear,
					            	   int dayOfMonth,
					            	   int hourOfDay,
					            	   int minuteOfHour,
					            	   int secondOfMinute) {
		return new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute);
	}
	
	/**
	 * 创建一个本地日期，只需要年、月、日（不带时、分、秒、毫秒）
	 * @param year
	 * @param monthOfYear
	 * @param dayOfMonth
	 * @return
	 */
	public static LocalDate getDate(int year,
            				 		int monthOfYear,
            				 		int dayOfMonth) {
		return new LocalDate(year, monthOfYear, dayOfMonth);
	}
	
	/**
	 * 创建一个本地时间，只需要时、分、秒、毫秒（不带年、月、日）
	 * @param hourOfDay
	 * @param minuteOfHour
	 * @param secondOfMinute
	 * @param millisOfSecond
	 * @return
	 */
	public static LocalTime getTime(int hourOfDay,
            				 		int minuteOfHour,
            				 		int secondOfMinute,
            				 		int millisOfSecond) {
		return new LocalTime(hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
	}
	/****************** 创建日期时间对象 end ************************/

	/****************** 日期加减 start ************************/
	/**
	 * 日期加减主要利用以下几个方法：
	 * 	minusDays()/minusMonths()/minusYears()
	 *  plusDays()/plusMonths()/plusYears()
	 */
	/**
	 * 获取昨天的日期
	 */
	public static DateTime yesterday() {
		return someDaysAgo(new DateTime(), 1);
	}
	
	/**
	 * 获取n天前的日期
	 * @param dateTime
	 * @param n
	 * @return
	 */
	public static DateTime someDaysAgo(DateTime dateTime, int n) {
		return dateTime.minusDays(n);
	}
	
	/**
	 * 获取明天的日期
	 * @return
	 */
	public static DateTime tomorrow() {
		return someDaysAfter(new DateTime(), 1);
	}
	
	/**
	 * 获取指定时间seconds秒后的时间
	 * @param dateTime
	 * @param seconds
	 * @return
	 */
	public static DateTime someSecondsAfter(DateTime dateTime, int seconds) {
		return dateTime.plusSeconds(seconds);
	}
	
	/**
	 * 获取指定时间seconds秒前的时间
	 * @param dateTime
	 * @param seconds
	 * @return
	 */
	public static DateTime someSecondsBefore(DateTime dateTime, int seconds) {
		return dateTime.minusSeconds(seconds);
	}
	
	/**
	 * 获取指定日期days天后的日期
	 * @param dateTime
	 * @param days
	 * @return
	 */
	public static DateTime someDaysAfter(DateTime dateTime, int days) {
		return dateTime.plusDays(days);
	}
	
	/**
	 * 获取上个月的日期
	 * @return
	 */
	public static DateTime lastMonth() {
		return someMonthsAgo(new DateTime(), 1);
	}
	
	/**
	 * 获取指定日期months个月之前的日期
	 * @param dateTime
	 * @param months
	 * @return
	 */
	public static DateTime someMonthsAgo(DateTime dateTime, int months) {
		return dateTime.minusMonths(months);
	}
	
	/**
	 * 获取下个月的日期
	 */
	public static DateTime nextMonth() {
		return someMonthsAfter(new DateTime(), 1);
	}
	
	/**
	 * 获取指定日期months个月之后的日期
	 * @param dateTime
	 * @param months
	 * @return
	 */
	public static DateTime someMonthsAfter(DateTime dateTime, int months) {
		return dateTime.plusMonths(months);
	}
	
	/**
	 * 获取去年的日期
	 */
	public static DateTime lastYear() {
		return someYearsAgo(new DateTime(), 1);
	}
	
	/**
	 * 获取指定日期n年前的日期
	 * @param dateTime
	 * @param years
	 * @return
	 */
	public static DateTime someYearsAgo(DateTime dateTime, int years) {
		return dateTime.minusYears(years);
	}
	
	/**
	 * 获取明年的日期
	 */
	public static DateTime nextYear() {
		return someYearsAfter(new DateTime(), 1);
	}
	
	/**
	 * 获取指定日期n年后的日期
	 * @param dateTime
	 * @param years
	 * @return
	 */
	public static DateTime someYearsAfter(DateTime dateTime, int years) {
		return dateTime.plusYears(years);
	}
	/****************** 日期加减 end ************************/
	
	/****************** 获取特殊日期 start ************************/
	/**
	 * 获取本月最后1天的日期
	 */
	public static DateTime getLastDayOfMonth() {
		return new DateTime().dayOfMonth().withMaximumValue();
	}
	
	/**
	 * 获取本月第1天的日期
	 */
	public static DateTime getFirstDayOfMonth() {
		return new DateTime().dayOfMonth().withMinimumValue();
	}
	
	/**
	 * 获取本周的第1天的日期（中国星期规则）
	 */
	public static DateTime getFirstDayOfWeek() {
		return new DateTime().dayOfWeek().withMinimumValue();
	}
	
	/**
	 * 获取本周的最后1天（中国星期规则）
	 */
	public static DateTime getLastDayOfWeek() {
		return new DateTime().dayOfWeek().withMaximumValue();
	}
	
	/**
	 * 获取第n天后的那一周的周一的日期（中国星期规则）
	 */
	public static DateTime getFirstDayOfNDaysAfter(int n) {
		return new DateTime().plusDays(n).dayOfWeek().withMinimumValue();
	}
	
	/**
	 * 获取指定日期所在的周属于该年的第几周
	 */
	public static int getWeekOfYear(DateTime dateTime) {
		return dateTime.getWeekOfWeekyear();
	}
	
	/**
	 * 获取指定日期所在周的日期列表（从周一到周日）（中国星期规则）
	 */
	public static List<String> getDateListOfWeek(DateTime dateTime){
		List<String> dateList = new ArrayList<>();
		DateTime firstDay = dateTime.dayOfWeek().withMinimumValue();
		dateList.add(dateTimeToString(firstDay, DATE_PATTERN_DEFAULT));
		dateList.add(dateTimeToString(someDaysAfter(firstDay, 1), DATE_PATTERN_DEFAULT));
		dateList.add(dateTimeToString(someDaysAfter(firstDay, 2), DATE_PATTERN_DEFAULT));
		dateList.add(dateTimeToString(someDaysAfter(firstDay, 3), DATE_PATTERN_DEFAULT));
		dateList.add(dateTimeToString(someDaysAfter(firstDay, 4), DATE_PATTERN_DEFAULT));
		dateList.add(dateTimeToString(someDaysAfter(firstDay, 5), DATE_PATTERN_DEFAULT));
		dateList.add(dateTimeToString(someDaysAfter(firstDay, 6), DATE_PATTERN_DEFAULT));
		return dateList;
	}
	
	/**
	 * 获取指定两个日期之间的所有日期列表（中国星期规则）
	 */
	public static List<String> getDateListOfTwoDate(DateTime start, DateTime end) {
		List<String> dateList = new ArrayList<>();
		// 先计算出两个日期之间相差的天数
		int daysInterval = getDaysInterval(start, end);
		dateList.add(dateTimeToString(start, DATE_PATTERN_DEFAULT));
		for (int i=1; i<=daysInterval; i++) {
			dateList.add(dateTimeToString(someDaysAfter(start, i), DATE_PATTERN_DEFAULT));
		}
		return dateList;
	}
	
	/**
	 * 获取指定两个日期之间的所有日期列表（倒序）
	 */
	public static List<String> getDateListOfTwoDateDesc(DateTime start, DateTime end) {
		List<String> dateList = new ArrayList<>();
		// 先计算出两个日期之间相差的天数
		int daysInterval = getDaysInterval(start, end);
		dateList.add(dateTimeToString(end, DATE_PATTERN_DEFAULT));
		for (int i=1; i<=daysInterval; i++) {
			dateList.add(dateTimeToString(someDaysAgo(end, i), DATE_PATTERN_DEFAULT));
		}
		return dateList;
	}
	
	/**
	 * 获取上周日的日期
	 */
	public static DateTime lastSunday() {
		return someDaysAgo(getLastDayOfWeek(), 7);
	}
	/****************** 获取特殊日期 end ************************/

	/****************** 计算日期或时间差 start ************************/
	/**
	 * 计算两个日期相差的年份数
	 * Interval 为半开 区间，这表示由 Interval 封装的时间跨度包括这段时间的起始时刻，但是不包含结束时刻。
	 * @param start
	 * @param end
	 * @return
	 */
	public static int getYearsInterval(DateTime start, DateTime end) {
        return Years.yearsBetween(start, end).getYears();
	}
	
	/**
	 * 计算两个日期相差的月份数
	 * Interval 为半开 区间，这表示由 Interval 封装的时间跨度包括这段时间的起始时刻，但是不包含结束时刻。
	 * @param start
	 * @param end
	 * @return
	 */
	public static int getMonthsInterval(DateTime start, DateTime end) {
        return Months.monthsBetween(start, end).getMonths();
	}
	
	/**
	 * 计算两个日期相差的天数
	 * Interval 为半开 区间，这表示由 Interval 封装的时间跨度包括这段时间的起始时刻，但是不包含结束时刻。
	 * @param start
	 * @param end
	 * @return
	 */
	public static int getDaysInterval(DateTime start, DateTime end) {
		return Days.daysBetween(start, end).getDays();
	}
	
	/**
	 * 获取两个日期相差的毫秒数
	 * Duration：表示一个绝对的精确跨度，单位:毫秒。
	 * @param start
	 * @param end
	 * @return
	 */
	public static long getMillsInterval(DateTime start, DateTime end) {
		Duration d = new Duration(start, end);
		return d.getMillis();
	}
	
	/**
	 * 获取两个日期相差的秒数
	 * @param start
	 * @param end
	 * @return
	 */
	public static long getSecondsInterval(DateTime start, DateTime end) {
		Duration d = new Duration(start, end);
		return d.getMillis() / 1000;
	}
	
	/**
	 * 获取今天剩余的秒数
	 * 
	 */
	public static long getRemaindSecondsToTodayEnd() {
		DateTime start = now();
//		DateTime end = new DateTime(today() + "T23:59:59"); // 第1种办法
		DateTime end = start.dayOfWeek().roundCeilingCopy();// 第2种办法
		return getSecondsInterval(start, end);
	}
	/****************** 计算日期或时间差 end ************************/

	/****************** 日期比较 start ************************/
	/**
	 * start是否在end之前
	 * @param start
	 * @param end
	 * @return
	 */
	public static boolean isBefore(DateTime start, DateTime end) {
		return start.isBefore(end);
	}
	
	/**
	 * start是否在end之后
	 * @param start
	 * @param end
	 * @return
	 */
	public static boolean isAfter(DateTime start, DateTime end) {
		return start.isAfter(end);
	}
	
	/**
	 * dateTime 是否早于当前时间
	 */
	public static boolean isBeforeNow(DateTime dateTime) {
		return dateTime.isBeforeNow();
	}
	
	/**
	 * dateTime 是否晚于当前时间
	 */
	public static boolean isAfterNow(DateTime dateTime) {
		return dateTime.isAfterNow();
	}
	
	/**
	 * dateTime 是否等于当前时间
	 */
	public static boolean isEqualNow(DateTime dateTime) {
		return dateTime.isEqualNow();
	}
	
	/**
	 * 是否为闰年
	 */
	public static boolean isLeapYear(DateTime dateTime) {
		return dateTime.year().isLeap();
	}
	
	/**
	 * startDate是否在endDate之前
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public static boolean isBefore(String startDate, String endDate) {
		return isBefore(
				JodaUtil.stringToDateTime(startDate, JodaUtil.DATE_PATTERN_DEFAULT), 
				JodaUtil.stringToDateTime(startDate, JodaUtil.DATE_PATTERN_DEFAULT)
			);
	}
	
	/**
	 * startDate是否在endDate之后
	 * @param startDate
	 * @param endDate
	 * @return
	 */
	public static boolean isAfter(String startDate, String endDate) {
		return isAfter(
				JodaUtil.stringToDateTime(startDate, JodaUtil.DATE_PATTERN_DEFAULT), 
				JodaUtil.stringToDateTime(startDate, JodaUtil.DATE_PATTERN_DEFAULT)
			);
	}
	
	/**
	 * startDate是否在今天之前
	 * @param strDate
	 * @return
	 */
	public static boolean isBeforeNow(String strDate) {
		return isBeforeNow(JodaUtil.stringToDateTime(strDate, JodaUtil.DATE_PATTERN_DEFAULT));
	}
	
	/**
	 * startDate是否在今天之后
	 * @param strDate
	 * @return
	 */
	public static boolean isAfterNow(String strDate) {
		return isAfterNow(JodaUtil.stringToDateTime(strDate, JodaUtil.DATE_PATTERN_DEFAULT));
	}
	/****************** 日期比较 end ************************/
	
	/****************** 日期转换为字符串 start ************************/
	/**
	 * 默认格式化
	 * @param dateTime
	 * @return
	 */
	public static String dateTimeToString(DateTime dateTime) {
		return dateTimeToString(dateTime, DATETIME_PATTERN_DEFAULT);
	}
	
	/**
	 * 日期格式化输出
	 * @param dateTime
	 * @param pattern
	 * @return
	 */
	public static String dateTimeToString(DateTime dateTime, String pattern) {
		return dateTime.toString(pattern);
	}
	
	/**
	 * 获取今天的日期字符串
	 */
	public static String today() {
		return dateTimeToString(now(), DATE_PATTERN_DEFAULT);
	}
	
	/**
	 * 获取今天的日期字符串
	 */
	public static String today(String pattern) {
		return dateTimeToString(now(), pattern);
	}
	
	/**
	 * 获取当前时间字符串
	 */
	public static String currentTime() {
		return dateTimeToString(now(), DATETIME_PATTERN_DEFAULT);
	}
	
	/**
	 * 获取当月的日期字符串
	 */
	public static String currentMonth() {
		String today = today(DATE_PATTERN_YYYYMMDD);
		return today.substring(0, 6);
	}
	/****************** 日期转换为字符串 end ************************/

	/****************** 字符串转换为日期 start ************************/
	/**
	 * 将字符串转换为日期对象
	 * @param jodaTime：格式：yyyy-MM-ddThh:mm:ss
	 * @return
	 */
	public static DateTime stringToDateTime(String jodaTime) {
		return new DateTime(jodaTime);
	}
	
	/**
	 * 将指定格式的JDK时间字符串转换为Joda日期对象
	 * @param jdkTime
	 * @return
	 */
	public static DateTime stringToDateTime(String jdkTime, String pattern) {
		Date date = null;
		try {
			date = (new SimpleDateFormat(pattern)).parse(jdkTime);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		if (date != null) {
			return new DateTime(date);
		} else {
			return null;
		}
	}
	
	/**
	 * 将Joda日期对象转换为指定格式的JDK日期时间
	 * @param dateTime
	 * @return
	 */
	public static Date dateTimeToJdkTime(DateTime dateTime) {
		Date date = null;
		try {
			String jdkTime = dateTimeToString(dateTime);
			date = (new SimpleDateFormat(DATETIME_PATTERN_DEFAULT)).parse(jdkTime);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;
	}
	/****************** 字符串转换为日期 end ************************/
	
	/**
     * Java将Unix时间戳转换成指定格式日期字符串
     * @param unixTimeSecond unix时间戳（秒） 如："1473048265";
     * @param pattern 要格式化的格式 默认："yyyy-MM-dd HH:mm:ss";
     *
     * @return 返回结果 如："2016-09-05 16:06:42";
     */
    public static String TimeStamp2Date(Long unixTimeSecond, String pattern) {
        return new SimpleDateFormat(pattern, Locale.CHINA).format(new Date(unixTimeSecond * 1000));
    }
    
    /**
     * 获取系统启动时间戳，作为静态资源版本号，格式：yyyyMMddHHmm
     */
    public static String getVersionNo() {
        return new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
    }
    
    /**
     * 判断今天是否过了12点
     * @return
     */
	public static boolean isAfter12() {
		String time12 = today() + " 12:00:00";
		DateTime time12Date = stringToDateTime(time12, DATETIME_PATTERN_DEFAULT);
		return isBeforeNow(time12Date);
	}

    /**
     * 判断今天是否为周日
     * @return
     */
	public static boolean isSundayToday() {
		DateTime sundayDate = getLastDayOfWeek();
		String sundayStr = dateTimeToString(sundayDate, DATE_PATTERN_DEFAULT);
		String today = today(DATE_PATTERN_DEFAULT);
		return sundayStr.equals(today);
	}
	
	/**
	 * 判断是否为大额逆镜像时间段
	 */
	public static boolean isBigInverselyTime() {
		if(isSundayToday() && isAfterNoon()) { // 周日 && 下午
			return true;
		}
		return false;
	}
	
    /**
     * 判断当前时刻是否为下午
     * @return
     */
	public static boolean isAfterNoon() {
		DateTime now = now();
		int hour = now.getHourOfDay();
		if(hour>=12 && hour<18) { // [12, 18)
			return true;
		}
		return false;
	}
	
    /**
     * 判断当前时刻是否为正午
     * @return
     */
	public static boolean isMidNoon() {
		DateTime now = now();
		int hour = now.getHourOfDay();
		if(hour>11 && hour<13) {
			return true;
		}
		return false;
	}
	
    /**
     * 判断当前时刻是否为深夜
     * @return
     */
	public static boolean isMidNight() {
		DateTime now = now();
		int hour = now.getHourOfDay();
		if(hour>23 || hour<6) {
			return true;
		}
		return false;
	}
    
	/**
	 * main
	 */
	public static void main(String[] args) {
		DateTime now = JodaUtil.now();
		System.out.println("now: " + now);
		System.out.println("currentTime:" + JodaUtil.currentTime());
		System.out.println("today: " + JodaUtil.today());
		System.out.println("yesterday: " +JodaUtil.dateTimeToString(JodaUtil.yesterday()));
		System.out.println("9000 seconds after:" + JodaUtil.someSecondsAfter(now, 9000));
		System.out.println("3600 seconds before:" + JodaUtil.dateTimeToString(JodaUtil.someSecondsBefore(JodaUtil.now(), 3600)));
		System.out.println("stringToDateTime: " + JodaUtil.stringToDateTime("2018-06-19 17:09:10", DATETIME_PATTERN_DEFAULT));
		System.out.println("weekOfYear: " + JodaUtil.getWeekOfYear(now));
		System.out.println("remaindSecondsToTodayEnd: " + JodaUtil.getRemaindSecondsToTodayEnd());
		System.out.println("isLeapYear: " + JodaUtil.isLeapYear(now));
		List<String> dateList = getDateListOfWeek(now);
		System.out.println("dateList of now: ");
		for(String str : dateList) {
			System.out.println(str);
		}
		List<String> dateList1 = getDateListOfTwoDate(now, new DateTime("2019-08-21T10:00:01"));
		System.out.println("dateList1 between two date: ");
		for(String str : dateList1) {
			System.out.println(str);
		}
		List<String> dateList2 = getDateListOfTwoDateDesc(new DateTime("2019-07-31T10:00:01"), now);
		System.out.println("dateList2 between two date: ");
		for(String str : dateList2) {
			System.out.println(str);
		}
		System.out.println("getDaysInterval:" + getDaysInterval(new DateTime("2018-09-16T20:00:01"), now));
		System.out.println("getMonthsInterval:" + getMonthsInterval(new DateTime("2018-07-16T20:00:01"), now));
		System.out.println("getYearsInterval:" + getYearsInterval(new DateTime("2017-09-18T20:00:01"), now));
		
		DateTime start = JodaUtil.stringToDateTime("2018-09-01", JodaUtil.DATE_PATTERN_DEFAULT);
        int daysInterval = JodaUtil.getDaysInterval(start, JodaUtil.now());
        System.out.println("daysInterval:" + daysInterval);
        System.out.println("versionNo:" + getVersionNo());
        System.out.println("isAfter12:" + isAfter12());
        System.out.println("isSundayToday:" + isSundayToday());
        System.out.println(JodaUtil.today(JodaUtil.DATETIME_PATTERN_YYYYMMDDHHMMSS));
        System.out.println(dateTimeToJdkTime(new DateTime("2019-10-01T10:00:01")));
        System.out.println(get0OclockOfToday());
	}
	
}
